/*
  使用方法:
    1、npm install scrollboundary
    2、import ScrollBoundary from 'scrollboundary'
    3、let s = new ScrollBoundary(el)   el: 滚动的容器dom元素  默认不传是body (不需要设置高度和overflow: auto, 内置了)
    (如果不是body元素, 建议设置固定高度，如果需要响应式高度，则需要calc计算成要固定的高度即可)
    4、s.start(callback)  callback: 回调函数, 形参就是是否滚动到了边界
 */
class Bus {
  constructor() {
    this.allEvent = Object.create(null)
  }

  // 发布
  on(eventType, handler) {
    this.allEvent[eventType] = this.allEvent[eventType] || []
    this.allEvent[eventType].push(handler)
  }

  // 订阅
  emit(eventType, ...args) {
    if(this.allEvent[eventType]){
      this.allEvent[eventType].forEach(handler => {
        handler(...args)
      })
    }
  }

  // 取消订阅
  off(eventType) {
    if(this.allEvent[eventType]) this.allEvent[eventType] = []
  }

  // 全部取消
  close(){
    this.allEvent = Object.create(null)
  }
}

// 判断是否滚动到最底部
export default class ScrollBoundary extends Bus {
  #el
  #top
  #height
  #lock = true
  #screenHeight = window.innerHeight
  #cb = () => {}
  // 节流
  #cut = (cb) => {
    if(this.#lock) {
      this.#lock = false
      setTimeout(() => {
        this.#lock = true
        cb()
      }, 60)
    }
  }

  // 私有处理是否到底触顶方法
  #topBottomFn = (e, init) => {
    let screen = this.#screenHeight - (this.#screenHeight - this.#height)
    // 判断思路 滚上去的距离 + 屏幕的高度 - (屏幕的高度 - 这个容器的高度)  == 滚动容器的高度    为到底部
    if(!init) {
      if(e.target.scrollTop + screen  == e.target.scrollHeight) this.emit('toBottom', { message: '已经到达指定容器的-->底部', to: 'bottom' })
      if(e.target.scrollTop == 0) this.emit('toBottom', { message: '已经到达指定容器的-->顶部', to: 'top' })
    }else{
      if(e.scrollTop + screen > e.scrollHeight) this.emit('toBottom', { message: '已经到达指定容器的-->底部', to: 'bottom' })
      if(e.scrollTop == 0) this.emit('toBottom', { message: '已经到达指定容器的-->顶部', to: 'top' })
    }
  }

  #noBody = (el) => {
    let fragment = document.createDocumentFragment('div')
    let div = document.createElement('div')
    div.classList.add('scroll-wrap-boundary')
    Array.from(el.children).forEach(item => {
      div.appendChild(item)
    })
    fragment.appendChild(div)
    document.body.appendChild(fragment)
    this.#el = div
  }

  constructor(el = document.body) {
    super()
    if(el.nodeName === 'BODY') this.#noBody(el)
    else this.#el = el
    getComputedStyle(this.#el).overflow == 'visible' ? this.#el.style.overflow = 'auto' : '' // 设置容器滚动否则不生效
    this.#top = el.getBoundingClientRect().top
    this.#height = el.getBoundingClientRect().height
  }

  start(cb) {
    let that = this
    this.#cb = cb
    this.on('toBottom', this.#cb)
    this.#topBottomFn(this.#el, 'init')
    this.#el.addEventListener('scroll', e => that.#cut(() => that.#topBottomFn(e)))
  }
}
